/*
 * IServiceUtil
 * 
 * Version information
 * 
 * Mar 17, 2006
 * 
 * Copyright (c) AmSoft Systems, 2006
 */
package net.amsoft.iservice.util;

import java.net.URL;
import java.rmi.server.UID;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Random;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import java.util.Date;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import org.apache.log4j.Logger;
import org.openxri.XRI;

import static net.amsoft.iservice.util.IServiceConstants.*;
import sun.misc.BASE64Encoder;
import sun.misc.BASE64Decoder;

/**
 * @author rahul
 * @author mohit
 */
public class IServiceUtil {
	private static byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8,
			(byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03};
	private static int iterationCount = 19;
	static Cipher ecipher;
	static Cipher dcipher;
	public static Logger oLog = Logger.getLogger(IServiceUtil.class);

	public static final String getOneWayEncValue(String key) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			// Create the message
			byte[] msg = key.getBytes();
			// Update the message digest with some more bytes
			// This can be performed multiple times before creating the hash
			md.update(msg);
			// Create the digest from the message
			byte[] aMessageDigest = md.digest();
			return new String(new BASE64Encoder().encode(aMessageDigest));
		} catch (Exception e) {
			return null;
		}
	}

	public static String getTwoWayEncValue(String sStringToEnc, String sPassKey) {
		try {
			// Create the key
			KeySpec keySpec = new PBEKeySpec(sPassKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance(
					"PBEWithMD5AndTripleDES").generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());
			// Pre pare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);
			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
			// Encode the string into bytes using utf-8
			byte[] utf8 = sStringToEnc.getBytes("UTF8");
			// Encrypt
			byte[] enc = ecipher.doFinal(utf8);
			// Encode bytes to base64 to get a string
			String sEncodedStr = new BASE64Encoder().encode(enc).replaceAll(
					"/", "-").replaceAll("\\+", "*");
			if (sEncodedStr.charAt(sEncodedStr.length() - 2) == '=')
				sEncodedStr = sEncodedStr
						.substring(0, sEncodedStr.length() - 2).concat("2");
			else if (sEncodedStr.charAt(sEncodedStr.length() - 1) == '=')
				sEncodedStr = sEncodedStr
						.substring(0, sEncodedStr.length() - 1).concat("1");
			else
				sEncodedStr = sEncodedStr.concat("0");
			return (sEncodedStr);
		} catch (Exception e) {
			return null;
		}
	}

	public static byte[] getSHABytes() {
		byte[] iDigest = null;
		try {
			// prefixing the current time in milliseconds to the
			// message digest input in order to have uniqueness
			Long lngCurTime = new Long((new Date()).getTime());
			String strCurTime = lngCurTime.toString();
			byte bytCurTime[] = strCurTime.getBytes();
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			// get xsid and cookie_id
			int reqLength = 32 - bytCurTime.length;
			if (reqLength <= 0) {// what century will this happen :)?
				reqLength = 32;// don't take a chance
			}
			byte rndId[] = new byte[reqLength];
			random.nextBytes(rndId);// put random bytes in remaining length
			byte tmpId[] = new byte[32];
			// concatentate both byte arrays in tmpXsid and hope hat is unique
			int ctr = 0;
			for (ctr = 0; ctr < bytCurTime.length; ctr++) {
				tmpId[ctr] = bytCurTime[ctr];
				if (ctr == 31) {
					// what century wil this happen :)?
					break;// don't take a chance
				}
			}
			if (ctr < 31) {
				// go in only if space remaning, then fill till 32
				// are filled
				for (int tmpCtr = 0; (tmpCtr < rndId.length) && (ctr < 32); tmpCtr++, ctr++) {
					tmpId[ctr] = rndId[tmpCtr];
				}
			}
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			iDigest = md.digest(tmpId);// digest's length = 20
		} catch (Exception e) {
			e.printStackTrace();
		}
		return iDigest;
	}// Method : getSHAValue

	public static final byte[] getSHABytes(String key) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-1");
			// Create the message
			byte[] msg = key.getBytes();
			// Update the message digest with some more bytes
			// This can be performed multiple times before creating the hash
			md.update(msg);
			// Create the digest from the message
			byte[] aMessageDigest = md.digest();
			return aMessageDigest;
		} catch (Exception e) {
			return null;
		}
	}

	public static String getTwoWayDecValue(String sStrToDec, String sPassKey) {
		try {
			String sDecodedStr = sStrToDec.replaceAll("-", "/").replaceAll(
					"\\*", "+");
			switch (sDecodedStr.charAt(sDecodedStr.length() - 1)) {
				case '0' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1);
					break;
				case '1' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1).concat("=");
					break;
				case '2' :
					sDecodedStr = sDecodedStr.substring(0,
							sDecodedStr.length() - 1).concat("==");
					break;
			}
			// Create the key
			KeySpec keySpec = new PBEKeySpec(sPassKey.toCharArray(), salt,
					iterationCount);
			SecretKey key = SecretKeyFactory.getInstance(
					"PBEWithMD5AndTripleDES").generateSecret(keySpec);
			ecipher = Cipher.getInstance(key.getAlgorithm());
			dcipher = Cipher.getInstance(key.getAlgorithm());
			// Pre pare the parameter to the ciphers
			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
					iterationCount);
			// Create the ciphers
			ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
			// Decode base64 to get bytes
			byte[] dec = new BASE64Decoder().decodeBuffer(sDecodedStr);
			// Decrypt
			byte[] utf8 = dcipher.doFinal(dec);
			// Decode using utf-8
			return new String(utf8, "UTF8");
		} catch (Exception e) {
			return null;
		}
	}

	public static String generateUniqueID() {
		UID oUID = new UID();
		Random oRnd = new Random();
		byte[] bytes = new byte[21];
		bytes[20] = 59;
		String ID = oUID.toString() + Float.toString(oRnd.nextFloat());
		byte[] shaBytes = getSHABytes(ID);
		for (int i = 0; i < 20; i++) {
			bytes[i] = shaBytes[i];
		}
		return new BASE64Encoder().encode(bytes);
	}

	/**
	 * Checks the null for the variable arguments passed
	 * 
	 * @param oArgs
	 * @return boolean
	 */
	public static boolean isNull(Object... oArgs) {
		for (int i = 0; i < oArgs.length; i++)
			if (oArgs[i] == null)
				return (true);
		return (false);
	}

	public static void log(int iLevel, Object oCallingObj, String... sArgs) {
		try {
			StringBuffer sMessage = new StringBuffer(oCallingObj.getClass()
					.getSimpleName()
					+ ": ");
			for (int i = 0; i < sArgs.length; i++) {
				sMessage.append(sArgs[i] + ", ");
			}
			switch (iLevel) {
				case DEBUG :
					oLog.debug(sMessage);
					break;
				case INFO :
					oLog.info(sMessage);
					break;
				case WARN :
					oLog.warn(sMessage);
					break;
				case ERROR :
					oLog.error(sMessage);
					break;
				case FATAL :
					oLog.fatal(sMessage);
					break;
				default :
					oLog.info(sMessage);
					break;
			}
		} catch (RuntimeException e) {
		}
	}
    
    
    /**
     * Extracts i-name from input text. Input has be a valid XRI or HXRI. HXRI
     * domain (xri.*) are read from the iservice properties and are
     * configurable. Embedded or naked i-name should start with a GCS
     * symbol(=,@,!)
     * 
     * @param sInput
     *            String from which i-name is to be extracted
     * @return Authority component of the XRI(i-name) extracted from input. <BR>
     *         <code>null</code>: If input is not a valid XRI/HXRI or the
     *         extracted i-name foes not start with a GCS char
     */
    public static String getIName(String sInput){
        String sIname = null;
        
        sIname = getXri(sInput);
        if(sIname != null)
            return sIname;
        
        //If not an XRI, check for HXRI
        try {
            URL oUrl = new URL(sInput);
            String sHxriDomains = DOMAINS_HXRI;
            
            if(sHxriDomains.contains(oUrl.getAuthority())){
                sIname = oUrl.getPath().substring(1);
                sIname = getXri(sIname);
            }
        } catch (Exception e){
            return null;
        }
        return sIname;
    }
    
    private static String getXri(String sXri){
        String sIname = null;
        
        try{
            XRI oXri = new XRI(sXri);
            sIname = oXri.getAuthorityPath().toString();
            if(! ("=@!".contains(String.valueOf(sIname.charAt(0))))){
                return null;
            }else
                return sIname;
        }catch (Exception e) {
            sIname = null;
        }
        
        return sIname;
    }
}
